﻿using System;
using System.Collections.Generic;
using System.IO;

namespace luozhuangSLLZSS
{
    public class LZSSInputStream
    {

        private static int N = 4096;        /* 4k buffers for LZ compression */
        private static int F = 18;          /* upper limit for LZ match length */
        private static int THRESHOLD = 2;   /* LZ encode string into pos and length 
                       if match size is greater than this */
        /* for reading LZ files */
        int state;                       /* where have we got to? */
        int i, j, k, r, c;
        int flags;
        byte[] text_buf;              /* ring buffer, with F-1 extra bytes 
                       for string comparison */

        Stream inStream;
        public LZSSInputStream(Stream inStream)
        {
            this.inStream = inStream;
            text_buf = new byte[N + F - 1];
        }

        /* pack_read: 
         *  Called by refill_buffer(). Unpacks from dat into buf, until either 
         *  EOF is reached or s bytes have been extracted. Returns the number of 
         *  bytes added to the buffer 
         */

        /* code stream */
        /* bits flag , bit 1-quoted byte 0-packed string */
        /* follows 8 codes, types determined by flagbits */
        /* 1st code type is inStream the lowest flag bit       */
        /* packed string is encoded inStream 16 bits: */
        /* 12 bits buffer position, 4 bits size */
        /* 1st byte: low part of pos. 
           2nd byte: high 4 bits = 4 high bits of pos. 
                     low  4 bits = size - 2 */
        //read(byte[] b, int off, int len) 
        //从此输入流中将 len 个字节的数据读入一个字节数组中。
        public int read(byte[] buf, int bufi, int s)
            
        {
            int size = 0;
            if (state == 0)
            {
                r = N - F;   /* fingbuffer pos? */
                flags = 0; /* zero flags */
            }
            while (s > 0)
            {
                if (state == 2)
                {
                    /* output encoded string from buffer */
                    if (k <= j)
                    {
                        c = text_buf[(i + k) & (N - 1)];
                        text_buf[r++] = (byte)c;
                        buf[bufi++] = (byte)c;
                        r &= (N - 1);
                        k++;
                        --s;
                        size++;
                        continue;
                    }
                }
                /* test zda mame nacist dalsi flags byte */
                if (((flags >>= 1) & 256) == 0)
                {
                    if ((c = inStream.ReadByte()) == -1)
                        break;
                    flags = c | 0xFF00;        /* uses higher byte to count eight */
                }
                if ((flags & 1) == 1)
                {
                    /* quoted character */
                    if ((c = inStream.ReadByte()) == -1)
                        break;
                    text_buf[r++] = (byte)c; /* vlozit do bufferu */
                    r &= N - 1; /* korekce pozice v ring bufferu */
                    buf[bufi++] = (byte)c; /* vystup ven */
                    ++size;
                    --s;
                    state = 1;
                    continue;
                }
                /* encoded string */
                if ((i = inStream.ReadByte()) == -1)
                    break;
                if ((j = inStream.ReadByte()) == -1)
                    break;
                i |= ((j & 0xF0) << 4);      /* position */
                j = (j & 0x0F) + THRESHOLD;  /* size */
                k = 0;                       /* output str. pos */
                state = 2;
            } /* while s>0 */

            if (size == 0) return -1;
            else return size;
        }

        public int read()
        {
            byte[] b = new byte[1];
            int rc = read(b, 0, 1);
            if (rc == -1) return -1;
            byte z = b[0];
            return z & 0xFF;
        }
        public void close()
        {
            text_buf = null;

            inStream.Close();
        }
    }
}
